// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
/* Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * Lombo VISS-TOP controller register definitions
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 */

#include <mach/csp.h>
#include "../viss-const.h"
#include "viss_top_csp.h"

#define ENABLE_TRACE
#include <mach/debug.h>

#define VISS_ADPLL_TUNE0	(0x139F0000UL)
#define VISS_ADPLL_TUNE1	(0x57379120UL)

#define VISS_ADPLL_600M		(600 * 1000 * 1000UL)
#define VISS_ADPLL_600M_PREDIV	(1)
#define VISS_ADPLL_600M_FACTOR	(25)

/* this ADPLL is actually only 792M  */
#define VISS_ADPLL_800M		(800 * 1000 * 1000UL)
#define VISS_ADPLL_800M_PREDIV	(1)
#define VISS_ADPLL_800M_FACTOR	(33)

#define VISS_SCLK0_FREQ		(1188000000UL)
#define VISS_MINI_MODULE_CLK	(74250000UL)
#define VISS_MAX_MODULE_DIV	(VISS_SCLK0_FREQ / VISS_MINI_MODULE_CLK)
#define VISS_27M_DIV		(VISS_SCLK0_FREQ / 27000000UL)

typedef struct viss_mclk_cfg {
	u32	freq;
	u32	src;
	u32	div;
} viss_mclk_cfg_t;

viss_mclk_cfg_t g_mclk_freq[] = {
	{ 6000000,	0,	4,},
	{ 8000000,	0,	3,},
	{ 12000000,	0,	2,},
	{ 24000000,	0,	1,},
	{ 27000000,	1,	22,},
	{ 37125000,	1,	16,},
};

reg_viss_t *g_viss = (reg_viss_t *)BASE_VISS;

void csp_viss_top_set_register_base(void *base)
{
	g_viss = (reg_viss_t *)base;
}
/**
 * Select ADPLL as PLL_MUX clock source
 */
u32 csp_viss_top_sel_adpll(void)
{
	return 0;
}

/**
 * Setup ADPLL pre-divider value: 1 or 2
 * @div: ADPLL_PRE_DIV
 *	0: pre-divider is 1
 *	1: pre-divider is 2
 */
u32 csp_viss_top_adpll_prediv(u32 div)
{
	return 0;
}

u32 csp_viss_top_adpll_tune(u32 tune0, u32 tune1)
{
	reg_viss_adpll_tune0_t tmpreg0;
	reg_viss_adpll_tune1_t tmpreg1;

	PRT_TRACE_BEGIN("\n");
	tmpreg0.val = tune0;
	tmpreg1.val = tune1;

	WRITEREG32(&(g_viss->adpll_tune0), tmpreg0.val);
	WRITEREG32(&(g_viss->adpll_tune1), tmpreg1.val);
	PRT_TRACE_END("\n");

	return 0;
}

/* factor should between 13 to 34 */
u32 csp_viss_top_adpll_factor(u32 factor)
{
	reg_viss_adpll_fac_t tmpreg;

	PRT_TRACE_BEGIN("factor=%u\n", factor);
	tmpreg.val = 0;
	tmpreg.bits.n = factor;
	WRITEREG32(&(g_viss->adpll_fac), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Setup ADPLL
 * Notes:
 *	a) factor should between 13 to 34
 *	b) Before modify factor dynamically, gating the clock the working module
 */
u32 csp_viss_top_setup_adpll(u32 prediv, u32 factor)
{
	PRT_TRACE_BEGIN("prediv=%u,factor=%u\n", prediv, factor);
	csp_viss_top_adpll_tune(VISS_ADPLL_TUNE0, VISS_ADPLL_TUNE1);
	csp_viss_top_adpll_prediv(prediv);
	csp_viss_top_adpll_factor(factor);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Enable ADPLL
 */
u32 csp_viss_top_adpll_enable(void)
{
	reg_viss_adpll_fac_t tmpreg;

	PRT_TRACE_BEGIN("\n");
	tmpreg.val = READREG32(&(g_viss->adpll_fac));
	tmpreg.bits.en = 1;
	WRITEREG32(&(g_viss->adpll_fac), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Disable ADPLL
 */
u32 csp_viss_top_adpll_disable(void)
{
	reg_viss_adpll_fac_t tmpreg;

	PRT_TRACE_BEGIN("\n");
	tmpreg.val = READREG32(&(g_viss->adpll_fac));
	tmpreg.bits.en = 0;
	WRITEREG32(&(g_viss->adpll_fac), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * ADPLL frequency lock status
 */
u32 csp_viss_top_adpll_lock(void)
{
	reg_viss_adpll_stat_t tmpreg;

	PRT_TRACE_BEGIN("\n");
	tmpreg.val = READREG32(&(g_viss->adpll_stat));
	PRT_TRACE_END("tmpreg.bits.flock=%u,tmpreg.bits.elock=%u\n",
			tmpreg.bits.flock, tmpreg.bits.plock);

	return ((tmpreg.bits.flock == 1) && (tmpreg.bits.plock == 1));
}

u32 csp_viss_top_adpll_init(void)
{
	csp_viss_top_setup_adpll(VISS_ADPLL_800M_PREDIV,
				VISS_ADPLL_800M_FACTOR);
	csp_viss_top_sel_adpll();
	csp_viss_top_adpll_enable();

	return 0;
}

u32 csp_viss_top_wb_dma_init(void)
{
	reg_viss_wb_dma_cfg_t tmpreg;

	tmpreg.val = 0;
	tmpreg.val = READREG32(&(g_viss->wb_dma_cfg));
	tmpreg.bits.aclk_gat = 1;
	WRITEREG32(&(g_viss->wb_dma_cfg), tmpreg.val);

	tmpreg.val = READREG32(&(g_viss->wb_dma_cfg));
	tmpreg.bits.hclk_gat = 1;
	WRITEREG32(&(g_viss->wb_dma_cfg), tmpreg.val);

	tmpreg.val = READREG32(&(g_viss->wb_dma_cfg));
	tmpreg.bits.rst = 1;
	WRITEREG32(&(g_viss->wb_dma_cfg), tmpreg.val);
	return 0;
}

u32 csp_viss_top_wb_dma_reset(void)
{
	reg_viss_wb_dma_cfg_t tmpreg;

	tmpreg.val = 0;
	tmpreg.val = READREG32(&(g_viss->wb_dma_cfg));
	tmpreg.bits.rst = 0;
	WRITEREG32(&(g_viss->wb_dma_cfg), tmpreg.val);

	tmpreg.val = READREG32(&(g_viss->wb_dma_cfg));
	tmpreg.bits.aclk_gat = 0;
	WRITEREG32(&(g_viss->wb_dma_cfg), tmpreg.val);

	tmpreg.val = READREG32(&(g_viss->wb_dma_cfg));
	tmpreg.bits.hclk_gat = 0;
	WRITEREG32(&(g_viss->wb_dma_cfg), tmpreg.val);

	tmpreg.val = READREG32(&(g_viss->wb_dma_cfg));
	tmpreg.bits.aclk_gat = 1;
	WRITEREG32(&(g_viss->wb_dma_cfg), tmpreg.val);

	tmpreg.val = READREG32(&(g_viss->wb_dma_cfg));
	tmpreg.bits.hclk_gat = 1;
	WRITEREG32(&(g_viss->wb_dma_cfg), tmpreg.val);

	tmpreg.val = READREG32(&(g_viss->wb_dma_cfg));
	tmpreg.bits.rst = 1;
	WRITEREG32(&(g_viss->wb_dma_cfg), tmpreg.val);
	return 0;
}

/**
 * Init VIC module
 */
u32 csp_viss_top_vic_init(u32 id, u32 clk_freq)
{
	u32 clk_div;
	unsigned long reg = 0;
	reg_viss_vic_cfg_t tmpreg;

	PRT_TRACE_BEGIN("clk_freq=%u\n", clk_freq);

	switch (id) {
	case VISS_VIC:
		reg = (unsigned long)&(g_viss->vic_cfg);
		break;
	default:
		PRT_ERR("id=%d\n", id);
		return __LINE__;
	}

	clk_div = VISS_SCLK0_FREQ / clk_freq;
	if (clk_div > VISS_MAX_MODULE_DIV)
		clk_div = VISS_MAX_MODULE_DIV;

	/* setup module clock divider */
	tmpreg.val = 0;
	tmpreg.bits.clk_div = clk_div - 1;
	WRITEREG32(reg, tmpreg.val);

	/* setup module clock gating */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.clk_gat = 1;
	WRITEREG32(reg, tmpreg.val);

	/* setup AHB clock gating */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.hclk_gat = 1;
	WRITEREG32(reg, tmpreg.val);

	/* Release module reset */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.rst = 1;
	WRITEREG32(reg, tmpreg.val);

	tmpreg.val = READREG32(reg);
	tmpreg.bits.clk_src = 1;
	WRITEREG32(reg, tmpreg.val);

	PRT_TRACE_END("\n");
	return 0;
}

u32 csp_viss_top_vic_reset(u32 id)
{
	unsigned long reg = 0;
	reg_viss_vic_cfg_t tmpreg;

	switch (id) {
	case VISS_VIC:
		reg = (unsigned long)&(g_viss->vic_cfg);
		break;
	default:
		PRT_ERR("id=%d\n", id);
		return __LINE__;
	}

	PRT_TRACE_BEGIN("\n");

	tmpreg.val = 0;
	/* Release module reset */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.rst = 0;
	WRITEREG32(reg, tmpreg.val);

	/* setup module clock gating */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.clk_gat = 0;
	WRITEREG32(reg, tmpreg.val);

	/* setup AHB clock gating */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.hclk_gat = 0;
	WRITEREG32(reg, tmpreg.val);

	/* setup module clock gating */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.clk_gat = 1;
	WRITEREG32(reg, tmpreg.val);

	/* setup AHB clock gating */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.hclk_gat = 1;
	WRITEREG32(reg, tmpreg.val);

	/* Release module reset */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.rst = 1;
	WRITEREG32(reg, tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Init MCSI module
 */
u32 csp_viss_top_mcsi_init(u32 id, u32 clk_freq)
{
	u32 clk_div;
	unsigned long reg = 0;
	reg_viss_mcsi_cfg_t tmpreg;

	/* clk_src default 0 */

	PRT_TRACE_BEGIN("clk_freq=%u\n", clk_freq);

	switch (id) {
	case VISS_MIPI_CSI:
		reg = (unsigned long)&(g_viss->mcsi_cfg);
		break;
	default:
		PRT_ERR("id=%d\n", id);
		return __LINE__;
	}

	clk_div = VISS_SCLK0_FREQ / clk_freq;
	if (clk_div > VISS_MAX_MODULE_DIV)
		clk_div = VISS_MAX_MODULE_DIV;

	/* setup module clock divider */
	tmpreg.val = 0;
	tmpreg.bits.clk_div = clk_div - 1;
	WRITEREG32(reg, tmpreg.val);

	/* setup module clock gating */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.clk_gat = 1;
	WRITEREG32(reg, tmpreg.val);

	/* setup module config clock gating */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.cfgclk_gat = 1;
	WRITEREG32(reg, tmpreg.val);

	/* setup AHB clock gating */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.hclk_gat = 1;
	WRITEREG32(reg, tmpreg.val);

	/* Release module reset */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.rst = 1;
	WRITEREG32(reg, tmpreg.val);

	tmpreg.val = READREG32(reg);
	tmpreg.bits.clk_src = 1;
	WRITEREG32(reg, tmpreg.val);

	PRT_TRACE_END("\n");

	return 0;
}

u32 csp_viss_top_mcsi_reset(u32 id)
{
	reg_viss_mcsi_cfg_t tmpreg;
	unsigned long reg = 0;

	switch (id) {
	case VISS_MIPI_CSI:
		reg = (unsigned long)&(g_viss->mcsi_cfg);
		break;
	default:
		PRT_ERR("id=%d\n", id);
		return __LINE__;
	}

	/* assert module reset */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.rst = 0;
	WRITEREG32(reg, tmpreg.val);

	/* disable module clock gating */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.clk_gat = 0;
	WRITEREG32(reg, tmpreg.val);

	/* disable AHB clock gating */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.hclk_gat = 0;
	WRITEREG32(reg, tmpreg.val);

	/* setup module clock gating */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.clk_gat = 1;
	WRITEREG32(reg, tmpreg.val);

	/* setup AHB clock gating */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.hclk_gat = 1;
	WRITEREG32(reg, tmpreg.val);

	/* Release module reset */
	tmpreg.val = READREG32(reg);
	tmpreg.bits.rst = 1;
	WRITEREG32(reg, tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}
/**
 * Init ISP module,
 * The max frequency should be less than 288MHz
 * The min frequency should be more than 99MHz
 */
u32 csp_viss_top_isp_init(u32 clk_freq)
{
	reg_viss_isp_cfg_t tmpreg;
	u32 div;

	PRT_TRACE_BEGIN("clk_freq=%u\n", clk_freq);

	div = VISS_ADPLL_800M / clk_freq;

	if (div < 2)
		div = 2;

	/* setup module clock divider */
	tmpreg.val = 0;
	tmpreg.bits.clk_div = div - 1;
	WRITEREG32(&(g_viss->isp_cfg), tmpreg.val);

	/* setup module clock source, default use adpll */
	tmpreg.val = READREG32(&(g_viss->isp_cfg));
	tmpreg.bits.clk_src = 0;	/* 0: adpll, 1: perh0 */
	WRITEREG32(&(g_viss->isp_cfg), tmpreg.val);

	/* setup module clock gating */
	tmpreg.val = READREG32(&(g_viss->isp_cfg));
	tmpreg.bits.clk_gat = 1;
	WRITEREG32(&(g_viss->isp_cfg), tmpreg.val);

	/* setup AHB clock gating */
	tmpreg.val = READREG32(&(g_viss->isp_cfg));
	tmpreg.bits.hclk_gat = 1;
	WRITEREG32(&(g_viss->isp_cfg), tmpreg.val);

	/* Release module reset */
	tmpreg.val = READREG32(&(g_viss->isp_cfg));
	tmpreg.bits.rst = 1;
	WRITEREG32(&(g_viss->isp_cfg), tmpreg.val);

	PRT_TRACE_END("\n");

	return 0;
}

/**
 * ISP module enable/disable
 */
u32 csp_viss_top_isp_clk_enable(u32 en)
{
	reg_viss_isp_cfg_t tmpreg;

	PRT_TRACE_BEGIN("\n");

	/* close module clock gating */
	tmpreg.val = READREG32(&(g_viss->isp_cfg));
	tmpreg.bits.clk_gat = en;
	WRITEREG32(&(g_viss->isp_cfg), tmpreg.val);
	/* close AHB clock gating */
	tmpreg.val = READREG32(&(g_viss->isp_cfg));
	tmpreg.bits.hclk_gat = en;
	WRITEREG32(&(g_viss->isp_cfg), tmpreg.val);
	/* assert module reset */
	tmpreg.val = READREG32(&(g_viss->isp_cfg));
	tmpreg.bits.rst = en;
	WRITEREG32(&(g_viss->isp_cfg), tmpreg.val);

	PRT_TRACE_END("\n");
	return 0;
}
/**
 * Init VIC MCLK
 */
u32 csp_viss_top_vic_mclk_init(u32 id, u32 clk_freq)
{
	u32 i;
	unsigned long reg = 0;

	reg_viss_vic_cfg_t tmpreg;

	PRT_TRACE_BEGIN("clk_freq=%u\n", clk_freq);
	switch (id) {
	case VISS_VIC:
		reg = (unsigned long)&(g_viss->vic_cfg);
		break;
	default:
		PRT_ERR("id=%d\n", id);
		return __LINE__;
	}

	/* MCLK default is 24M*/
	for (i = 0; i < ARRAY_SIZE(g_mclk_freq); i++) {
		if (clk_freq == g_mclk_freq[i].freq)
			break;
	}

	if (i >= ARRAY_SIZE(g_mclk_freq)) {
		PRT_ERR("Not supported mclk frequency (%d) !\n",
			clk_freq);
		return __LINE__;
	}

	/*setup MCLK clock divider*/
	tmpreg.val = READREG32(reg);
	tmpreg.bits.mclk_div = g_mclk_freq[i].div - 1;
	WRITEREG32(reg, tmpreg.val);

	/*setup MCLK clock source*/
	tmpreg.val = READREG32(reg);
	tmpreg.bits.mclk_src = g_mclk_freq[i].src;
	WRITEREG32(reg, tmpreg.val);

	/*setup MCLK clock gating*/
	tmpreg.val = READREG32(reg);
	tmpreg.bits.mclk_gat = 1;
	WRITEREG32(reg, tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

u32 csp_viss_top_vic_mclk_gate(u32 id, u32 enable)
{
	unsigned long reg = 0;
	reg_viss_vic_cfg_t tmpreg;

	PRT_TRACE_BEGIN("enable=%u\n", enable);

	switch (id) {
	case VISS_VIC:
		reg = (unsigned long)&(g_viss->vic_cfg);
		break;
	default:
		PRT_ERR("id=%d\n", id);
		return __LINE__;
	}
	tmpreg.val = READREG32(reg);
	tmpreg.bits.mclk_gat = enable;
	WRITEREG32(reg, tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Init MCSI MCLK
 */
u32 csp_viss_top_mcsi_mclk_init(u32 id, u32 clk_freq)
{
	u32 i;
	unsigned long reg = 0;
	reg_viss_mcsi_cfg_t tmpreg;

	PRT_TRACE_BEGIN("clk_freq=%u\n", clk_freq);

	switch (id) {
	case VISS_MIPI_CSI:
		reg = (unsigned long)&(g_viss->mcsi_cfg);
		break;
	default:
		PRT_ERR("id=%d\n", id);
		return __LINE__;
	}

	/* MCLK default is 24M*/
	for (i = 0; i < ARRAY_SIZE(g_mclk_freq); i++) {
		if (clk_freq == g_mclk_freq[i].freq)
			break;
	}

	if (i >= ARRAY_SIZE(g_mclk_freq)) {
		PRT_ERR("Not supported mclk frequency (%d) !\n",
			clk_freq);
		return __LINE__;
	}

	/*setup MCLK clock divider*/
	tmpreg.val = READREG32(reg);
	tmpreg.bits.mclk_div = g_mclk_freq[i].div - 1;
	WRITEREG32(reg, tmpreg.val);

	/*setup MCLK clock source*/
	tmpreg.val = READREG32(reg);
	tmpreg.bits.mclk_src = g_mclk_freq[i].src;
	WRITEREG32(reg, tmpreg.val);

	/*setup MCLK clock gating*/
	tmpreg.val = READREG32(reg);
	tmpreg.bits.mclk_gat = 1;
	WRITEREG32(reg, tmpreg.val);

	PRT_TRACE_END("\n");
	return 0;
}

u32 csp_viss_top_mcsi_mclk_gate(u32 id, u32 enable)
{
	unsigned long reg = 0;
	reg_viss_mcsi_cfg_t tmpreg;

	PRT_TRACE_BEGIN("enable=%u\n", enable);

	switch (id) {
	case VISS_MIPI_CSI:
		reg = (unsigned long)&(g_viss->mcsi_cfg);
		break;
	default:
		PRT_ERR("id=%d\n", id);
		return __LINE__;
	}
	tmpreg.val = READREG32(reg);
	tmpreg.bits.mclk_gat = enable;
	WRITEREG32(reg, tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}
/**
 * Select ISP pixel channel data source
 */
u32 csp_viss_top_isp_data_source(u32 src)
{
	reg_viss_isp_cfg_t tmpreg;

	PRT_TRACE_BEGIN("src=%u\n", src);
	tmpreg.val = READREG32(&(g_viss->isp_cfg));
	tmpreg.bits.data_src = src;
	WRITEREG32(&(g_viss->isp_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Select ISP_LITE pixel channel data source
 */
u32 csp_viss_top_isp_lite_data_source(u32 src)
{
	return 0;
}

/******************************************************************************
 * sau ldo
 *****************************************************************************/

void csp_viss_cam_ldo_enable(void)
{

}

void csp_viss_cam_ldo_disable(void)
{

}
